"""
交易产品基础信息模块
获取和管理交易所的交易产品信息
"""
import time
from typing import Dict, List, Optional
from datetime import datetime, timedelta
from loguru import logger

try:
    import okx.PublicData as PublicData
except ImportError:
    logger.warning("okx SDK未安装，请运行: pip install okx")
    PublicData = None


class InstrumentInfoManager:
    """交易产品信息管理器"""
    
    # 限速配置：20次/2s
    RATE_LIMIT_REQUESTS = 20
    RATE_LIMIT_WINDOW = 2.0  # 秒
    
    def __init__(self, sandbox: bool = True):
        """
        初始化产品信息管理器
        
        Args:
            sandbox: 是否使用模拟盘，True=模拟盘(1)，False=实盘(0)
        """
        if PublicData is None:
            raise ImportError("okx SDK未安装，请运行: pip install okx")
        
        self.sandbox = sandbox
        flag = "1" if sandbox else "0"
        
        # 初始化OKX公共数据API
        self.public_api = PublicData.PublicAPI(flag=flag)
        
        # 限速控制
        self.request_times: List[float] = []  # 请求时间戳列表
        
        # 数据缓存
        self.instruments_cache: Dict[str, List[Dict]] = {}  # instType -> instruments
        self.cache_expire_time: Dict[str, datetime] = {}  # instType -> expire_time
        self.cache_duration = timedelta(hours=1)  # 缓存1小时
        
        logger.info(f"交易产品信息管理器已初始化 (模式: {'模拟盘' if sandbox else '实盘'})")
    
    def _check_rate_limit(self):
        """
        检查并执行限速
        限速规则：20次/2s (IP + Instrument Type)
        """
        now = time.time()
        
        # 移除2秒前的请求记录
        self.request_times = [t for t in self.request_times 
                             if now - t < self.RATE_LIMIT_WINDOW]
        
        # 如果达到限速上限，等待
        if len(self.request_times) >= self.RATE_LIMIT_REQUESTS:
            oldest_request = self.request_times[0]
            wait_time = self.RATE_LIMIT_WINDOW - (now - oldest_request)
            
            if wait_time > 0:
                logger.debug(f"触发限速，等待 {wait_time:.2f} 秒")
                time.sleep(wait_time)
                # 重新计算
                now = time.time()
                self.request_times = [t for t in self.request_times 
                                     if now - t < self.RATE_LIMIT_WINDOW]
        
        # 记录本次请求
        self.request_times.append(now)
    
    def get_instruments(self, inst_type: str, uly: Optional[str] = None,
                       inst_family: Optional[str] = None,
                       inst_id: Optional[str] = None,
                       use_cache: bool = True) -> List[Dict]:
        """
        获取交易产品基础信息
        
        Args:
            inst_type: 产品类型
                - SPOT: 币币
                - MARGIN: 币币杠杆
                - SWAP: 永续合约
                - FUTURES: 交割合约
                - OPTION: 期权
            uly: 标的指数，如 BTC-USD（仅适用于交割/永续/期权）
            inst_family: 交易品种，如 BTC-USD（仅适用于交割/永续/期权）
            inst_id: 产品ID，如 BTC-USDT-SWAP
            use_cache: 是否使用缓存
            
        Returns:
            产品信息列表，每个产品包含以下字段：
            - instType: 产品类型
            - instId: 产品ID
            - uly: 标的指数
            - instFamily: 交易品种
            - baseCcy: 交易货币
            - quoteCcy: 计价货币
            - settleCcy: 盈亏结算和保证金币种
            - ctVal: 合约面值
            - ctMult: 合约乘数
            - ctValCcy: 合约面值计价币种
            - optType: 期权类型（C: 看涨，P: 看跌）
            - stk: 行权价格
            - listTime: 上线时间
            - expTime: 到期时间
            - lever: 最大杠杆倍数
            - tickSz: 下单价格精度
            - lotSz: 下单数量精度
            - minSz: 最小下单数量
            - ctType: 合约类型（linear: 正向，inverse: 反向）
            - alias: 合约日期别名
            - state: 产品状态（live: 交易中，suspend: 暂停，preopen: 预上线）
        """
        # 检查缓存
        cache_key = f"{inst_type}_{uly}_{inst_family}_{inst_id}"
        if use_cache and cache_key in self.instruments_cache:
            expire_time = self.cache_expire_time.get(cache_key)
            if expire_time and datetime.now() < expire_time:
                logger.debug(f"从缓存获取产品信息: {inst_type}")
                return self.instruments_cache[cache_key]
        
        # 执行限速检查
        self._check_rate_limit()
        
        # 构建请求参数
        params = {"instType": inst_type}
        if uly:
            params["uly"] = uly
        if inst_family:
            params["instFamily"] = inst_family
        if inst_id:
            params["instId"] = inst_id
        
        try:
            logger.info(f"获取交易产品信息: {params}")
            result = self.public_api.get_instruments(**params)
            
            # 检查响应
            if result.get("code") != "0":
                error_msg = result.get("msg", "未知错误")
                logger.error(f"获取产品信息失败: {error_msg}")
                raise RuntimeError(f"API错误: {error_msg}")
            
            # 提取数据
            instruments = result.get("data", [])
            logger.info(f"获取产品信息成功: {len(instruments)} 个产品")
            
            # 更新缓存
            if use_cache:
                self.instruments_cache[cache_key] = instruments
                self.cache_expire_time[cache_key] = datetime.now() + self.cache_duration
            
            return instruments
            
        except Exception as e:
            logger.error(f"获取产品信息异常: {e}")
            raise
    
    def get_instrument_by_id(self, inst_id: str, use_cache: bool = True) -> Optional[Dict]:
        """
        根据产品ID获取单个产品信息
        
        Args:
            inst_id: 产品ID，如 BTC-USDT-SWAP
            use_cache: 是否使用缓存
            
        Returns:
            产品信息字典，如果不存在返回None
        """
        # 从inst_id推断产品类型
        if "-SWAP" in inst_id:
            inst_type = "SWAP"
        elif "-" in inst_id and inst_id.count("-") == 2:
            # 可能是期货或期权，需要进一步判断
            parts = inst_id.split("-")
            if len(parts[2]) == 6 and parts[2].isdigit():
                inst_type = "FUTURES"
            else:
                inst_type = "OPTION"
        else:
            inst_type = "SPOT"
        
        try:
            instruments = self.get_instruments(
                inst_type=inst_type,
                inst_id=inst_id,
                use_cache=use_cache
            )
            
            if instruments:
                return instruments[0]
            return None
            
        except Exception as e:
            logger.error(f"获取产品信息失败: {inst_id}, {e}")
            return None
    
    def get_spot_instruments(self, base_ccy: Optional[str] = None,
                            use_cache: bool = True) -> List[Dict]:
        """
        获取现货交易对信息
        
        Args:
            base_ccy: 基础货币，如 BTC
            use_cache: 是否使用缓存
            
        Returns:
            现货交易对列表
        """
        instruments = self.get_instruments("SPOT", use_cache=use_cache)
        
        # 如果指定了基础货币，进行过滤
        if base_ccy:
            instruments = [inst for inst in instruments 
                          if inst.get("baseCcy") == base_ccy]
        
        return instruments
    
    def get_swap_instruments(self, uly: Optional[str] = None,
                            use_cache: bool = True) -> List[Dict]:
        """
        获取永续合约信息
        
        Args:
            uly: 标的指数，如 BTC-USD
            use_cache: 是否使用缓存
            
        Returns:
            永续合约列表
        """
        return self.get_instruments("SWAP", uly=uly, use_cache=use_cache)
    
    def get_futures_instruments(self, uly: Optional[str] = None,
                               use_cache: bool = True) -> List[Dict]:
        """
        获取交割合约信息
        
        Args:
            uly: 标的指数，如 BTC-USD
            use_cache: 是否使用缓存
            
        Returns:
            交割合约列表
        """
        return self.get_instruments("FUTURES", uly=uly, use_cache=use_cache)
    
    def get_trading_pairs(self, inst_type: str = "SWAP",
                         base_currencies: Optional[List[str]] = None) -> List[str]:
        """
        获取可交易的交易对列表
        
        Args:
            inst_type: 产品类型
            base_currencies: 基础货币列表，如 ['BTC', 'ETH']
            
        Returns:
            交易对ID列表
        """
        instruments = self.get_instruments(inst_type)
        
        # 过滤状态为live的产品
        live_instruments = [inst for inst in instruments 
                           if inst.get("state") == "live"]
        
        # 如果指定了基础货币，进行过滤
        if base_currencies:
            live_instruments = [inst for inst in live_instruments
                               if inst.get("baseCcy") in base_currencies or
                                  inst.get("uly", "").split("-")[0] in base_currencies]
        
        # 提取产品ID
        trading_pairs = [inst.get("instId") for inst in live_instruments]
        
        return trading_pairs
    
    def get_instrument_details(self, inst_id: str) -> Dict:
        """
        获取产品的详细交易规则
        
        Args:
            inst_id: 产品ID
            
        Returns:
            包含交易规则的字典：
            - min_size: 最小下单数量
            - tick_size: 价格精度
            - lot_size: 数量精度
            - contract_value: 合约面值
            - max_leverage: 最大杠杆
        """
        instrument = self.get_instrument_by_id(inst_id)
        
        if not instrument:
            raise ValueError(f"产品不存在: {inst_id}")
        
        return {
            "inst_id": inst_id,
            "inst_type": instrument.get("instType"),
            "min_size": float(instrument.get("minSz", 0)),
            "tick_size": float(instrument.get("tickSz", 0)),
            "lot_size": float(instrument.get("lotSz", 0)),
            "contract_value": float(instrument.get("ctVal", 1)),
            "contract_mult": float(instrument.get("ctMult", 1)),
            "max_leverage": float(instrument.get("lever", 1)),
            "base_ccy": instrument.get("baseCcy", ""),
            "quote_ccy": instrument.get("quoteCcy", ""),
            "settle_ccy": instrument.get("settleCcy", ""),
            "state": instrument.get("state", ""),
        }
    
    def clear_cache(self):
        """清除所有缓存"""
        self.instruments_cache.clear()
        self.cache_expire_time.clear()
        logger.info("产品信息缓存已清除")
    
    def get_cache_stats(self) -> Dict:
        """
        获取缓存统计信息
        
        Returns:
            缓存统计字典
        """
        total_cached = len(self.instruments_cache)
        valid_cached = sum(1 for key in self.instruments_cache.keys()
                          if key in self.cache_expire_time and
                          datetime.now() < self.cache_expire_time[key])
        
        return {
            "total_cached": total_cached,
            "valid_cached": valid_cached,
            "expired_cached": total_cached - valid_cached,
            "cache_duration_hours": self.cache_duration.total_seconds() / 3600,
        }


# 便捷函数
def get_instruments(inst_type: str, sandbox: bool = True, **kwargs) -> List[Dict]:
    """
    便捷函数：获取交易产品信息
    
    Args:
        inst_type: 产品类型 (SPOT/SWAP/FUTURES/OPTION)
        sandbox: 是否使用模拟盘
        **kwargs: 其他参数传递给get_instruments
        
    Returns:
        产品信息列表
    """
    manager = InstrumentInfoManager(sandbox=sandbox)
    return manager.get_instruments(inst_type, **kwargs)


def get_trading_pairs(inst_type: str = "SWAP", sandbox: bool = True,
                     base_currencies: Optional[List[str]] = None) -> List[str]:
    """
    便捷函数：获取可交易的交易对列表
    
    Args:
        inst_type: 产品类型
        sandbox: 是否使用模拟盘
        base_currencies: 基础货币列表
        
    Returns:
        交易对ID列表
    """
    manager = InstrumentInfoManager(sandbox=sandbox)
    return manager.get_trading_pairs(inst_type, base_currencies)
